package com.jxtc.enterprise.common.scheduler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jxtc.enterprise.common.constants.DeliveryCostTypeEnum;
import com.jxtc.enterprise.common.dto.DailyOrderCountAndAmountDTO;
import com.jxtc.enterprise.common.entity.DeliveryTask;
import com.jxtc.enterprise.common.entity.DeliveryTaskStore;
import com.jxtc.enterprise.common.entity.DeliveryTaskTemplate;
import com.jxtc.enterprise.common.entity.DeliveryTaskTemplateStore;
import com.jxtc.enterprise.common.mapper.*;
import com.jxtc.enterprise.common.utils.DateTimeUtil;
import com.jxtc.enterprise.delivery.dto.todayTask.CompanyIdWithMealTypeDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 配送任务定时任务，用于根据配送任务模板表 delivery_task_template_t 中的在指定时间生成/更新配送任务表 delivery_task_t
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class DeliveryTaskScheduler {

    private final DeliveryTaskTemplateMapper deliveryTaskTemplateMapper;
    private final DeliveryTaskTemplateStoreMapper deliveryTaskTemplateStoreMapper;
    private final DeliveryTaskMapper deliveryTaskMapper;
    private final DeliveryTaskStoreMapper deliveryTaskStoreMapper;
    private final OrderDetailMapper orderDetailMapper;

    /**
     * 每天晚上 23 点执行一次
     * 每天 23 点，将所有配送任务状态更新为 "已完成"，作为兜底措施，避免骑手配送完成后，忘记在配送员 H5 上点击 "已送达" 按钮
     */
    @Scheduled(cron = "0 0 23 * * ?")
    public void updateDeliveryTaskToCompleted() {
        log.info("updateDeliveryTaskToCompleted, executing...");
        // 获取当天日期
        String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 1. 查询今天所有未删除且未标记为 "已完成" 的配送任务
        LambdaQueryWrapper<DeliveryTask> queryWrapper = new LambdaQueryWrapper<DeliveryTask>()
                .eq(DeliveryTask::getDelFlag, false)
                .eq(DeliveryTask::getDeliveryStatus, 1)
                .eq(DeliveryTask::getDeliveryDate, today)
                .select(DeliveryTask::getId);
        List<String> deliveryTaskIdList = deliveryTaskMapper.selectObjs(queryWrapper);
        log.info("updateDeliveryTaskToCompleted, 今日未手动点击已送达的配送任务数量: {}", deliveryTaskIdList.size());

        // 2. 更新这些配送任务的状态为 "已完成"
        for (String taskId : deliveryTaskIdList) {
            DeliveryTask deliveryTask = new DeliveryTask();
            deliveryTask.setId(taskId);
            // 将配送任务完成状态标记为 2（已完成）
            deliveryTask.setDeliveryStatus(2);

            int updateCount = deliveryTaskMapper.updateById(deliveryTask);
            if (updateCount != 1) {
                log.error("updateDeliveryTaskToCompleted, update delivery task status failed, deliveryTaskId: {}", taskId);
            }

            // 3. 更新配送任务与店铺关联表中的取餐状态，未取餐的需要标记为已取餐
            // 既然配送任务已完成，那么说明已经去店铺取餐了。保证数据库中数据的一致性
            LambdaUpdateWrapper<DeliveryTaskStore> taskStoreQueryWrapper = new LambdaUpdateWrapper<DeliveryTaskStore>()
                    .eq(DeliveryTaskStore::getTaskId, taskId)
                    .eq(DeliveryTaskStore::getDelFlag, false)
                    .eq(DeliveryTaskStore::getPickUpStatus, 1);
            DeliveryTaskStore deliveryTaskStore = new DeliveryTaskStore();
            // 将取餐状态标记为 2（已取餐）
            deliveryTaskStore.setPickUpStatus(2);
            // 可能 delivery_task_store 中 pick_up_status 已经被全部标记为 2 了
            // 所以修改条数可能为 0，就不需要判断是否有记录被修改成功了
            deliveryTaskStoreMapper.update(deliveryTaskStore, taskStoreQueryWrapper);
        }

        log.info("updateDeliveryTaskToCompleted, executed");
    }


    /**
     * 每天凌晨 1 点执行一次
     * 每天凌晨 1 点，计算前一天配送任务的佣金
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void calculateDeliveryTaskCommission() {
        log.info("calculateDeliveryTaskCommission, executing...");

        // 获取昨天的日期
        String yesterday = LocalDate.now()
                .minusDays(1)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 1. 查询所有前一天未删除的配送任务
        LambdaQueryWrapper<DeliveryTask> queryWrapper = new LambdaQueryWrapper<DeliveryTask>()
                .eq(DeliveryTask::getDelFlag, false)
                .eq(DeliveryTask::getDeliveryDate, yesterday);
        List<DeliveryTask> deliveryTaskList = deliveryTaskMapper.selectList(queryWrapper);

        // 2. 计算相关费用
        for (DeliveryTask savedDeliveryTask : deliveryTaskList) {
            DeliveryTask deliveryTask = new DeliveryTask();
            deliveryTask.setId(savedDeliveryTask.getId());

            // 获取公司 ID
            CompanyIdWithMealTypeDTO company = deliveryTaskMapper.queryCompanyIdByTaskId(savedDeliveryTask.getId());
            String companyId = company.getCompanyId();
            String mealType = company.getMealType();

            // 查询该配送任务关联的店铺列表
            LambdaQueryWrapper<DeliveryTaskStore> taskStoreQueryWrapper = new LambdaQueryWrapper<DeliveryTaskStore>()
                    .eq(DeliveryTaskStore::getTaskId, savedDeliveryTask.getId())
                    .eq(DeliveryTaskStore::getDelFlag, false)
                    .select(DeliveryTaskStore::getStoreId);
            List<String> storeIdList = deliveryTaskStoreMapper.selectObjs(taskStoreQueryWrapper);

            // 计算当日配送任务的订单数量及订单金额
            int orderCount = 0;
            int orderAmount = 0;
            if (!storeIdList.isEmpty()) {
                // 根据公司 ID、店铺 ID 列表、订单所属日期查询当日的订单总数量及总金额
                DailyOrderCountAndAmountDTO orderCountAndAmount = orderDetailMapper.queryDailyOrderCountAndAmount(companyId, yesterday, mealType, storeIdList);
                if (orderCountAndAmount != null) {
                    orderCount = orderCountAndAmount.getOrderCount() == null ? 0 : orderCountAndAmount.getOrderCount();
                    orderAmount = orderCountAndAmount.getOrderAmount() == null ? 0 : orderCountAndAmount.getOrderAmount();
                }
            }

            int total = 0;
            if (DeliveryCostTypeEnum.PER_ORDER.getName().equals(deliveryTask.getDeliveryCostType())) {
                // 按订单数计算配送费用
                total = orderCount * deliveryTask.getDeliveryCostValue();
            } else if (DeliveryCostTypeEnum.PERCENTAGE_OF_ORDER.getName().equals(deliveryTask.getDeliveryCostType())) {
                // 按订单金额百分比计算配送费用（数据库中存储单位是 "万分之一"，所以需要除以 10000）
                total = orderAmount * deliveryTask.getDeliveryCostValue() / 10000;
            } else if (DeliveryCostTypeEnum.FIXED_AMOUNT.getName().equals(deliveryTask.getDeliveryCostType())) {
                // 固定金额
                total = deliveryTask.getDeliveryCostValue();
            }

            // 计算该配送任务的总费用，计算规则：根据配送费用类型计算的金额 + 费用加项 - 扣除费用
            int totalFee = total + savedDeliveryTask.getAdditionalFee() - savedDeliveryTask.getDeductFee();
            deliveryTask.setOrderCount(orderCount);
            deliveryTask.setOrderAmount(orderAmount);
            deliveryTask.setTotalFee(totalFee);

            int updateCount = deliveryTaskMapper.updateById(deliveryTask);
            if (updateCount != 1) {
                log.error("calculateDeliveryTaskCommission, update delivery task commission failed, deliveryTaskId: {}", savedDeliveryTask.getId());
            }
        }
        log.info("calculateDeliveryTaskCommission, executed");
    }


    /**
     * 每天凌晨 2 点执行一次
     * 每天凌晨 2 点，根据配送任务模板创建新一天的配送任务实例
     */
    @Scheduled(cron = "0 3 0 * * ?")
    public void createDeliveryTask() {
        log.info("createDeliveryTask, executing...");
        // 1. 查询所有未删除的配送任务模板
        LambdaQueryWrapper<DeliveryTaskTemplate> queryWrapper = new LambdaQueryWrapper<DeliveryTaskTemplate>()
                .eq(DeliveryTaskTemplate::getDelFlag, false);
        List<DeliveryTaskTemplate> deliveryTaskTemplateList = deliveryTaskTemplateMapper.selectList(queryWrapper);

        // 2. 根据租户 ID 分组，一个租户一个租户的新增，方便排查问题
        Map<String, List<DeliveryTaskTemplate>> templateGroupByTenantId = deliveryTaskTemplateList.stream()
                .collect(Collectors.groupingBy(DeliveryTaskTemplate::getTenantId));
        for (Map.Entry<String, List<DeliveryTaskTemplate>> entry : templateGroupByTenantId.entrySet()) {
            String tenantId = entry.getKey();
            List<DeliveryTaskTemplate> taskTemplateList = entry.getValue();
            log.info("createDeliveryTask, tenantId: {}, number of delivery task template: {}", tenantId, taskTemplateList.size());

            for (DeliveryTaskTemplate taskTemplate : taskTemplateList) {
                String todayDate = DateTimeUtil.getCurrentDate();
                LambdaQueryWrapper<DeliveryTask> todayDeliveryTaskQueryWrapper = new LambdaQueryWrapper<DeliveryTask>()
                        .eq(DeliveryTask::getTaskTemplateId, taskTemplate.getId())
                        .eq(DeliveryTask::getDeliveryDate, todayDate)
                        .eq(DeliveryTask::getDelFlag, false);
                if (!deliveryTaskMapper.exists(todayDeliveryTaskQueryWrapper)) {
                    // 如果当前配送任务模板没有今天的配送任务实例，则新增今日的配送任务实例
                    log.info("createDeliveryTask, tenantId: {}, taskTemplateId:{}, 新增今天的配送任务, deliveryDate: {}", tenantId, taskTemplate.getId(), todayDate);
                    createDeliveryTask(taskTemplate, todayDate, tenantId);
                }
                String tomorrowDate = DateTimeUtil.getTomorrowDate();
                LambdaQueryWrapper<DeliveryTask> tomorrowDeliveryTaskQueryWrapper = new LambdaQueryWrapper<DeliveryTask>()
                        .eq(DeliveryTask::getTaskTemplateId, taskTemplate.getId())
                        .eq(DeliveryTask::getDeliveryDate, tomorrowDate)
                        .eq(DeliveryTask::getDelFlag, false);
                if (!deliveryTaskMapper.exists(tomorrowDeliveryTaskQueryWrapper)) {
                    // 如果当前配送任务模板没有明天的配送任务实例，则新增明天的配送任务实例
                    log.info("createDeliveryTask, tenantId: {}, taskTemplateId:{}, 新增明天的配送任务, deliveryDate: {}", tenantId, taskTemplate.getId(), tomorrowDate);
                    createDeliveryTask(taskTemplate, tomorrowDate, tenantId);
                }
            }

            log.info("createDeliveryTask, tenantId: {} insert delivery_task_t completed", tenantId);
        }

        log.info("createDeliveryTask, executed");
    }

    private void createDeliveryTask(DeliveryTaskTemplate taskTemplate, String deliveryDate, String tenantId) {
        // 3. 根据配送任务模板 ID 查询与其关联的所有未删除的店铺
        LambdaQueryWrapper<DeliveryTaskTemplateStore> templateStoreQueryWrapper = new LambdaQueryWrapper<DeliveryTaskTemplateStore>()
                .eq(DeliveryTaskTemplateStore::getTaskTemplateId, taskTemplate.getId())
                .eq(DeliveryTaskTemplateStore::getDelFlag, false);
        List<DeliveryTaskTemplateStore> deliveryTaskTemplateStoreList = deliveryTaskTemplateStoreMapper.selectList(templateStoreQueryWrapper);

        // 4. 新增配送任务
        DeliveryTask deliveryTask = new DeliveryTask();
        deliveryTask.setTaskTemplateId(taskTemplate.getId());
        deliveryTask.setTenantId(tenantId);
        deliveryTask.setRiderId(taskTemplate.getRiderId());
        deliveryTask.setDeliveryDate(deliveryDate);

        deliveryTask.setDeliveryStartTime(taskTemplate.getDeliveryStartTime());
        deliveryTask.setDeliveryCostType(taskTemplate.getDeliveryCostType());
        deliveryTask.setDeliveryCostValue(taskTemplate.getDeliveryCostValue());
        deliveryTask.setDeliveryOrder(taskTemplate.getDeliveryOrder());

        int insert = deliveryTaskMapper.insert(deliveryTask);
        if (insert < 1) {
            log.error("createDeliveryTask, insert delivery task failed, tenantId: {}, taskTemplateId: {}", tenantId, taskTemplate.getTenantId());
        }

        // 获取新增配送任务后自动生成的配送任务 ID
        String taskId = deliveryTask.getId();

        // 5. 新增配送任务与店铺关联关系
        // TODO: 考虑是否批量新增，批量新增失败时则全部失败
        for (DeliveryTaskTemplateStore deliveryTaskTemplateStore : deliveryTaskTemplateStoreList) {
            DeliveryTaskStore deliveryTaskStore = new DeliveryTaskStore();
            deliveryTaskStore.setTaskId(taskId);
            deliveryTaskStore.setStoreId(deliveryTaskTemplateStore.getStoreId());

            int insertStore = deliveryTaskStoreMapper.insert(deliveryTaskStore);
            if (insertStore < 1) {
                log.error("createDeliveryTask, insert delivery_task_store failed, tenantId: {}, taskTemplateId: {}, taskId: {}, storeId: {}", tenantId, taskTemplate.getId(), taskId, deliveryTaskTemplateStore.getStoreId());
            }
        }
    }
}
